 /* -*-C-*-
 ##############################################################################
 #
 # File:        trice/src/io.c
 # RCS:         "@(#)$Revision: 1.17 $ $Date: 94/03/09 11:05:36 $"
 # Description: routines for low level I/O to E1430 module
 # Author:      Doug Passey
 # Created:     
 # Language:    C
 # Package:     E1430
 # Status:      "@(#)$State: Exp $"
 #
 # (C) Copyright 1992, Hewlett-Packard Company, all rights reserved.
 #
 ##############################################################################
 #
 # Please add additional comments here
 #
 # Revisions:
 #
 ##############################################################################
*/

#    include <stdio.h>
#    include <stdlib.h>
#    include <signal.h>
#    include <setjmp.h>

#include "trice.h"
#include "err1430.h"


#ifndef lint
const char io_fileId[] = "$Header: io.c,v 1.17 94/03/09 11:05:36 chriss Exp $";
#endif


/******************************************************************************
 *
 * This reports a bus error.
 *
 *****************************************************************************/
SHORTSIZ16 i1430_report_bus_error(SHORTSIZ16 la, SHORTSIZ16 offset)
{
  char buf[80];

  (void)sprintf(buf, "logical address = %d, register offset = 0x%X", 
					(LONGSIZ32)la, (LONGSIZ32)offset);
  return(i1430_Error(ERR1430_BUS_ERROR, buf, NULL));
}


/******************************************************************************
 *
 * turns debug level on 
 *
 *****************************************************************************/
void e1430_debug_level(SHORTSIZ16 level)
{
  e1430_debug = level;
}

/******************************************************************************
 *
 * Returns in <regPtr> a pointer to the correct register in the 
 * aModuleImage data structure given the VXI register, <reg>, 
 * and an index, <index> into the e1430_modState array.
 * Returns 0 if OK, else a negative error number.
 *
 *****************************************************************************/
static SHORTSIZ16 e1430_get_reg_ptr(SHORTSIZ16 index, SHORTSIZ16 reg, 
						SHORTSIZ16 **regPtr)
{
  char buf[80];

  if(index == -1) {
    return(i1430_Error(ERR1430_LA_NOT_IN_MOD_GROUP, NULL, NULL));
  }
    switch(reg) {		/* get current bits in register */
      case E1430_ANALOG_SETUP_REG:
        *regPtr = &(e1430_modStates[index].analogSetup);
        break;
      case E1430_INPUT_OFFSET_REG:
        *regPtr = &(e1430_modStates[index].offset);
        break;
      case E1430_MEAS_CONTROL_REG:
        *regPtr = &(e1430_modStates[index].measControl);
        break;
      case E1430_DATA_FORMAT_REG:
        *regPtr = &(e1430_modStates[index].dataFormat);
        break;
      case E1430_IRQ_CONFIG_0_REG:
        *regPtr = &(e1430_modStates[index].irqConfig0);
        break;
      case E1430_IRQ_CONFIG_1_REG:
        *regPtr = &(e1430_modStates[index].irqConfig1);
        break;
      case E1430_HP_PORT_CONTROL_REG:
        *regPtr = &(e1430_modStates[index].portControl);
        break;
      case E1430_TRIGGER_SETUP_REG:
        *regPtr = &(e1430_modStates[index].trigSetup);
        break;
      case E1430_TRIGGER_BLOCKSIZE_REG:
        *regPtr = &(e1430_modStates[index].trigBlocksize);
        break;
      case E1430_TRIGGER_OFFSET_LO_REG:
        *regPtr = &(e1430_modStates[index].trigOffset);
        break;
      case E1430_TIMING_SETUP_REG:
        *regPtr = &(e1430_modStates[index].timingSetup);
        break;
      case E1430_ADC_CONTROL_REG:
        *regPtr = &(e1430_modStates[index].adcControl);
        break;
      case E1430_TRIGGER_PASSOUT_REG:
        *regPtr = &(e1430_modStates[index].trigPassout);
        break;
      case E1430_ZOOM_PHASE_0_REG:
        *regPtr = &(e1430_modStates[index].zoomPhase0);
        break;
      case E1430_ZOOM_PHASE_1_REG:
        *regPtr = &(e1430_modStates[index].zoomPhase1);
        break;
      case E1430_ZOOM_INTERP_0_REG:
        *regPtr = &(e1430_modStates[index].zoomInterp0);
        break;
      case E1430_ZOOM_INTERP_1_REG:
        *regPtr = &(e1430_modStates[index].zoomInterp1);
        break;
      case E1430_ZOOM_INTERP_2_REG:
        *regPtr = &(e1430_modStates[index].zoomInterp2);
        break;
      case E1430_ZOOM_INTERP_3_REG:
        *regPtr = &(e1430_modStates[index].zoomInterp3);
        break;
      case E1430_ZOOM_CONTROL_REG:
        *regPtr = &(e1430_modStates[index].zoomControl);
        break;
      case TRIGGER_LEVEL_SAVE:
        *regPtr = &(e1430_modStates[index].trigLevelSave);
        break;
      case TRIGGER_SLOPE_SAVE:
        *regPtr = &(e1430_modStates[index].trigSlopeSave);
        break;
      case E1430_VXI_CONTROL_REG:
	return(-1);
      default:
        (void) (void)sprintf(buf, "%d", (LONGSIZ32)reg);
        return(i1430_Error(ERR1430_ILLEGAL_REGISTER_OFFSET, buf, NULL));

    }
  return (0);
}


/******************************************************************************
 *
 * Do all the weird trigger interaction stuff to set correct level and slope
 * due to inverting amp in lower ranges and different level values for mag
 * and adc triggering.  The parameters passed in are the index into the
 * modStates array of module images and the desired value of the trigger
 * setup register.  The algorithm to determine the correct value of the
 * trigger setup register is something like this:
 *
 * IF (trigger source is internal) {
 *   mask off level and slope bits from the value passed in
 *   get trigger level from trigLevelSave
 *
 *   IF (internal trigger is from ADC) {
 *
 *     IF (inverting amplifier is inserted between input and ADC) {
 *
 *	 slope = inversion of slope bit from trigSlopeSave
 *	 level = - adc level from trigLevelSave;
 *
 *     }ELSE{
 *
 *	 slope = slope bit from trigSlopeSave
 *	 level = adc level from trigLevelSave;
 *
 *     }
 *
 *   }ELSE( internal trigger is magnitude ) {
 *
 *      slope = slope bit from trigSlopeSave
 *      level = mag level from trigLevelSave;
 *
 *   }
 *
 * }ELSE( trig source is external ) {
 *   mask off slope bit from the value passed in
 *   slope = inversion of slope bit from trigSlopeSave
 * }
 *
 *****************************************************************************/
static void get_correct_trigger_setup_bits(SHORTSIZ16 index, SHORTSIZ16 *reg)
{ 
  SHORTSIZ16 outReg, level;

  if((*reg & ~TRIGGER_SETUP_SOURCE_MASK) == TRIGGER_SETUP_SOURCE_INT) {

    /* mask off level and slope bits */
    outReg = *reg & (TRIGGER_SETUP_SLOPE_MASK & TRIGGER_SETUP_LEVEL_MASK);

    if((*reg & ~TRIGGER_SETUP_INTERNAL_MASK) == TRIGGER_SETUP_INTERNAL_ADC) {
      
      /* check the range */
      if(e1430_modStates[index].analogSetup & ANALOG_SETUP_INPUT_AMP_ON) {

	if(e1430_modStates[index].trigSlopeSave == TRIGGER_SETUP_SLOPE_POS) {
	  outReg |= TRIGGER_SETUP_SLOPE_NEG;
	}else{
	  outReg |= TRIGGER_SETUP_SLOPE_POS;
	}

	level = e1430_modStates[index].trigLevelSave & ~TRIGGER_SETUP_LEVEL_MASK;
	outReg |= (255 - level);

      }else{		/* no inverting amp */
       
        outReg |= e1430_modStates[index].trigSlopeSave;
	outReg |= (e1430_modStates[index].trigLevelSave & 
					~TRIGGER_SETUP_LEVEL_MASK);
      }

    }else{	/* internal trigger is magnitude or off */

      outReg |= e1430_modStates[index].trigSlopeSave;
      outReg |= ((e1430_modStates[index].trigLevelSave >> 
		TRIGGER_LEVEL_SAVE_MAG_BIT_SHIFT) & ~TRIGGER_LEVEL_SAVE_ADC_MASK);
    }

  }else{			/* external trigger source */
    /* must invert sense of slope bit to compensate for inv buf in ext trig */
    outReg = *reg & TRIGGER_SETUP_SLOPE_MASK;

    if(e1430_modStates[index].trigSlopeSave == TRIGGER_SETUP_SLOPE_POS) {
      outReg |= TRIGGER_SETUP_SLOPE_NEG;
    }else{
      outReg |= TRIGGER_SETUP_SLOPE_POS;
    }

  }

  *reg = outReg;
}


/******************************************************************************
 *
 * Update a specific register <reg> in a group of modules, <groupID>.  First
 * clear the bits defined as 0 in <mask> and the OR in the data in <bits>.

 *****************************************************************************/
SHORTSIZ16 i1430_update_group_module_bits(SHORTSIZ16 groupID, SHORTSIZ16 reg,
				SHORTSIZ16 mask, SHORTSIZ16 bits)
{
  aModGroupPtr ptr;
  SHORTSIZ16 offsetBits, newBits, oldBits, regBits, *regPtr;
  SHORTSIZ16 error;
  SHORTSIZ16 i, la;
  SHORTSIZ16 rangeBits;

  if((ptr = i1430_valid_module_group(groupID)) == NULL) {/* no such group exists */
    return (ERR1430_NO_GROUP);
  }

  for(; *ptr != -1; ptr++) {
    la = e1430_modStates[*ptr].logicalAddr;

    error = e1430_get_reg_ptr(*ptr, reg, &regPtr);
    if(error) return (error);

    regBits = *regPtr & mask;   /* mask off bits that will change */
    regBits |= bits;		    /* set new bits into register */
    
    switch(reg) {	/* catch special cases */
      case TRIGGER_LEVEL_SAVE:
	*regPtr = regBits;
	break;

      case TRIGGER_SLOPE_SAVE:
	*regPtr = regBits;
	break;

      case E1430_ANALOG_SETUP_REG:  
	/* catch range change, do offset change and, if necessary, 
	 * flip trigger slope and and level if changing between range 
	 * with inverting amplifier and one without
	 */

        oldBits = *regPtr;
        
	error = e1430_write_register_image(la, reg, regBits);
	if(error) return(error);

	/* if range change, correct offset and maybe trig level and slope */
  	for(i=0; i<RANGE_STEPS; i++) {		/* get correct offset */
    	  i1430_get_range_bits_index(i, &rangeBits);
    	  if(rangeBits ==
		(e1430_modStates[*ptr].analogSetup & ~ANALOG_SETUP_RANGE_MASK)){
      	    offsetBits = e1430_modStates[*ptr].offsetSave[i];

	      
	    /* if range change involves changing state of inverting amp */
	    if((oldBits & ANALOG_SETUP_INPUT_AMP_ON) != 
				(regBits & ANALOG_SETUP_INPUT_AMP_ON)) {  
	      
	      oldBits = e1430_modStates[*ptr].trigSetup;

	      newBits = oldBits;
	      get_correct_trigger_setup_bits(*ptr, &newBits);
	
	      if(newBits != oldBits) {
		error = e1430_write_register_image(la, 
					E1430_TRIGGER_SETUP_REG, newBits);
	        if(error) return(error);
	      }

      	    }
	    
	    break;
    	  }
  	}
        error = e1430_write_register_image(la, 
					E1430_INPUT_OFFSET_REG, offsetBits);
	if(error) return(error);

	break;

      /* Need to catch cases where going into either TRIGGER_SETUP_SOURCE_ADC or
       * TRIGGER_SETUP_SOURCE_MAG without explicitly setting the level, or
       * case where we are in internal ADC triggering with the inverting
       * amp in the circuit (lower three ranges). 
       * Restore the correct level and slope from the trigLevelSave and
       * trigSlopeSave fields in the module image.  These fields must
       * already have been set correctly.
       */
      case E1430_TRIGGER_SETUP_REG:
	get_correct_trigger_setup_bits(*ptr, &regBits);
	
        error = e1430_write_register_image(la, reg, regBits);
	if(error) return(error);
	break;

      default:
        /* write register on module and update module state */
        error = e1430_write_register_image(la, reg, regBits);
	if(error) return(error);
	break;
    }
  }

  return (0);
}


/******************************************************************************
 * Writes a word, <data>, to an individual register, <offset> in each module 
 * in the module group, <groupID>.
 * Returns negative error number if error, else returns 0;
 *****************************************************************************/
SHORTSIZ16 e1430_write_register(SHORTSIZ16 groupID, SHORTSIZ16 offset, 
							SHORTSIZ16 data)
{
  SHORTSIZ16 error;

  error = i1430_update_group_module_bits(groupID, offset, 0, data);
  if(error) return (error);

  return (0);
}


/******************************************************************************
 * Writes a word of data to an individual register located at offset
 * above the base address of the module, as pointed to by the logical
 * address, la. Updates the image register also.
 * Returns negative error number if error, else returns 0;
 *****************************************************************************/
SHORTSIZ16 e1430_write_register_image(SHORTSIZ16 la, SHORTSIZ16 offset, 
							SHORTSIZ16 data)
{
  SHORTSIZ16 *regPtr;
  SHORTSIZ16 error;
  SHORTSIZ16 index;
  int jump;
  volatile USHORTSIZ16 *addr;

  if(e1430_debug > 0) {
    (void)printf("Register & image write: la = %hd, %s (%XH) = %0.4hXH\n", la, 
		i1430_get_register_name(offset, E1430_IMAGE_REGISTER), offset, data) ;
  }

    addr = (USHORTSIZ16 *)e1430_get_register_address(la, offset);

#ifdef RADISYS
   e1430_clear_berr();
#endif
  if(e1430_try) {     /* E1430_TRY */	
    jump = setjmp(e1430_env);		/* enable TRY-RECOVER */
  }else{
    jump = 0;		/* disable TRY-RECOVER */
  }
  if(!jump) {

    iwpoke( addr, data);
    E1430_CHECK_BERR
  
    /* update local module data structure */
    error = i1430_get_index_from_la(la, &index);  /* get index into e1430_modStates array */
    if(error) return (error);
  
    error = e1430_get_reg_ptr(index, offset, &regPtr);/* get pointer to image */
    if(error > 0) return (error);

    if(!error) {
      *regPtr = data;			/* update module data structure */
    }

  E1430_RECOVER {
    return (i1430_report_bus_error(la, offset));
  } 
   
  return (0);


}

/******************************************************************************
 * Writes a word of data to an individual register located at offset
 * above the base address of the module, as pointed to by the logical
 * address, la.  Doesn't update an image register.
 * Returns negative error number if error, else returns 0;
 *****************************************************************************/
SHORTSIZ16 e1430_write_register_card(SHORTSIZ16 la, SHORTSIZ16 offset, 
							SHORTSIZ16 data)
{
  int jump;
  volatile USHORTSIZ16 *addr;

  if(e1430_debug > 0) {
    (void)printf("Register only write: la = %hd, %s (%XH) = %0.4hXH\n", la, 
		i1430_get_register_name(offset, E1430_CARD_REGISTER), offset, data) ;
  }

  addr = (USHORTSIZ16 *)e1430_get_register_address(la, offset);

#ifdef RADISYS
   e1430_clear_berr();
#endif
  if(e1430_try) {     /* E1430_TRY */	
    jump = setjmp(e1430_env);		/* enable TRY-RECOVER */
  }else{
    jump = 0;		/* disable TRY-RECOVER */
  }
  if(!jump) {

    iwpoke( addr, data) ;
    E1430_CHECK_BERR

  E1430_RECOVER  {
    return (i1430_report_bus_error(la, offset));
  }

  return (0);


}

/******************************************************************************
 *
 * Reads word into data from an individual register, located at offset 
 * from logical address, la.  Returns negative error number if error, 
 * else returns 0;
 *
 *****************************************************************************/
SHORTSIZ16 e1430_read_register_card(SHORTSIZ16 la, SHORTSIZ16 offset,  
							SHORTSIZ16 *data)
{
  int jump;
  volatile USHORTSIZ16 *addr;

  addr = (USHORTSIZ16 *)e1430_get_register_address(la, offset);

#ifdef RADISYS
   e1430_clear_berr();
#endif
  if(e1430_try) {     /* E1430_TRY */	
    jump = setjmp(e1430_env);		/* enable TRY-RECOVER */
  }else{
    jump = 0;		/* disable TRY-RECOVER */
  }
  if(!jump) {

    *data = iwpeek( addr );
    E1430_CHECK_BERR

  E1430_RECOVER {
    return (i1430_report_bus_error(la, offset));
  }

  return (0);


}


/******************************************************************************
 *
 * Reads word into data from an individual register, <offset> of the module 
 * image corresponding to the module at located at logical address, <la>.  
 * Returns negative error number if error, * else returns 0;
 *
 *****************************************************************************/
SHORTSIZ16 e1430_read_register_image(SHORTSIZ16 la, SHORTSIZ16 offset,  
							SHORTSIZ16 *data)
{
  SHORTSIZ16 *addr;
  SHORTSIZ16 index;
  SHORTSIZ16 error;

  error = i1430_get_index_from_la(la, &index);
  if(error) return (error);

  error = e1430_get_reg_ptr(index, offset, &addr);
  if(error) return (error);

  *data = *addr;

  return (0);
}


/******************************************************************************
 * This function resets the E1430 module at logical address, la.
 *****************************************************************************/
SHORTSIZ16 i1430_reset_E1430(SHORTSIZ16 la)
{
  SHORTSIZ16 index, error;

  error = i1430_get_index_from_la(la, &index);
  if(error) return(error);

  /* turn off clock master to insure clearing of masters[] array */
  error = e1430_set_clock_master_mode(la, E1430_MASTER_CLOCK_OFF);
  if(error) return(error);

  (void)e1430_write_register_image(la, E1430_TIMING_SETUP_REG, 0);

  /* set analog setup to DC coupled, antialias, single-ended, range = 8 volt */
  (void)e1430_write_register_image(la, E1430_ANALOG_SETUP_REG,
				0x7 |
				ANALOG_SETUP_COUPLING_DC |
				ANALOG_SETUP_INPUT_HI_CONN|
				ANALOG_SETUP_INPUT_LO_FLOAT|
				ANALOG_SETUP_ANTIALIAS_ON);

  /* init trigger level to 0, and trigger slope to positive */
  (void)e1430_write_register_image(la, E1430_TRIGGER_SETUP_REG, 
				0x80 |		/* level 0.0 volts */
				TRIGGER_SETUP_TRIG_ENABLE |
				TRIGGER_SETUP_TRIG_AUTO_ON |
				TRIGGER_SETUP_SLOPE_POS |
				TRIGGER_SETUP_SOURCE_INT |
				TRIGGER_SETUP_INTERNAL_ADC);

  /* reset ADC control */
  (void)e1430_write_register_image(la, E1430_ADC_CONTROL_REG, 
  			ADC_CONTROL_CAL_MODE_NORMAL | ADC_CONTROL_DIAG_NORMAL);

  /* zero the input offset register */
  (void)e1430_write_register_image(la, E1430_INPUT_OFFSET_REG, 0x800);


  /* set to VME data port*/
  (void)e1430_write_register_image(la, E1430_HP_PORT_CONTROL_REG, 
   			PORT_CONTROL_SEND_PORT_VME | PORT_CONTROL_LBUS_DISABLE);

  /* set block size to 1024 */
  (void)e1430_write_register_image(la, E1430_TRIGGER_BLOCKSIZE_REG, 0x2400);

  /* zero out trigger delay */
  (void)e1430_write_register_image(la, E1430_TRIGGER_OFFSET_LO_REG, 0);

  /* data size = 32 and block mode */
  (void)e1430_write_register_image(la, E1430_DATA_FORMAT_REG, 
  			DATA_FORMAT_BLOCK_MODE_ON | DATA_FORMAT_DATA_SIZE_32);

  /* reset the DSP */
  (void)e1430_write_register_image(la, E1430_MEAS_CONTROL_REG, MEAS_CONTROL_RESET_DSP_MODE);
  (void)e1430_write_register_image(la, E1430_MEAS_CONTROL_REG, MEAS_CONTROL_SYNC_ON);
  (void)e1430_write_register_image(la, E1430_MEAS_CONTROL_REG, 
				MEAS_CONTROL_SYNC_OFF | MEAS_CONTROL_NORMAL_MODE);
 

  /* center frequency to zero */
  (void)e1430_write_register_image(la, E1430_ZOOM_PHASE_0_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_PHASE_1_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_INTERP_0_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_INTERP_1_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_INTERP_2_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_INTERP_3_REG, 0);
/*  e1430_write_register_image(la, E1430_ZOOM_CONTROL_REG, 14); */
  (void)e1430_write_register_image(la, E1430_ZOOM_CONTROL_REG, 1);
  (void)e1430_write_register_card(la, E1430_LO_TRANSFER_REG, 0);
  (void)e1430_write_register_image(la, E1430_ZOOM_CONTROL_REG, 7);

  return (0);
}


